package cn.stylefeng.guns.modular.gridsystem.websocket;

import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.LRUCache;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.stylefeng.guns.modular.gridsystem.enums.temp.TempConfigUtil;
import cn.stylefeng.guns.modular.gridsystem.enums.temp.TempDictType;
import cn.stylefeng.guns.modular.gridsystem.temperature.entity.BizTemperatureConfig;
import cn.stylefeng.guns.modular.gridsystem.temperature.model.param.BizTemperatureRecordParam;
import cn.stylefeng.guns.modular.gridsystem.temperature.service.BizTemperatureConfigService;
import cn.stylefeng.guns.modular.gridsystem.temperature.service.BizTemperatureRecordService;
import cn.stylefeng.guns.modular.gridsystem.utils.ColorMapUtil;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import top.blesslp.librosa.core.Librosa;
import top.blesslp.librosa.core.Utils;
import top.blesslp.librosa.core.model.SpectrumEntity;
import top.blesslp.librosa.core.model.SpectrumEnums;

import javax.sound.sampled.AudioFormat;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

@Slf4j
@Component
@ServerEndpoint(value = "/ws/gateway/{token}", configurator = WebSocketConfig.class)
public class WebSocketGatewayServer {

    Long start = 0L;
    private String newValue = "10";
    private static BizTemperatureRecordService bizTemperatureRecordService;

    @Autowired
    public void setBizTemperatureRecordService(BizTemperatureRecordService bizTemperatureRecordService) {
        WebSocketGatewayServer.bizTemperatureRecordService = bizTemperatureRecordService;
    }

    @Autowired
    private TempConfigUtil tempConfigUtil;

    LRUCache<String, String> TEMP_RECORD_INTERVAL_CACHE = CacheUtil.newLRUCache(100, 100L * 1000L);

  /*  public void setBizTemperatureRecordService(BizTemperatureRecordService bizTemperatureRecordService){
        WebSocketGatewayServer.bizTemperatureRecordService =bizTemperatureRecordService;
    }*/

    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    private final Gson gson = new Gson();
    private Session session;

    private static BizTemperatureRecordParam latestTemperature;

    private static final ConcurrentHashMap<String, Session> sessionMap = new ConcurrentHashMap<>();

    private static Map<String, Session> sendVibrationTarget = new HashMap<>();

    private static AtomicBoolean vibrationIsStart = new AtomicBoolean(false);

    private static final ExecutorService executorService = Executors.newFixedThreadPool(10);

//    @Value("${vibration-test.onOrOff}")
//    private String onOrOff;
//    static {
//        timer.schedule(new TimerTask() {
//            @Override
//            public void run() {
//                //System.out.println("1s!");
//                log.info("timer->"+System.currentTimeMillis());
//                if(V_Test.isSendDate){
//                    V_Test.isSendDate = false;
//                }
//            }
//        },60*1000,60*1000);
//    }

    /**
     * 发送消息
     *
     * @param session
     * @param message
     * @throws IOException
     */
    public void sendMessage(Session session, String message) throws IOException {
        if (session != null) {
            synchronized (session) {
                session.getBasicRemote().sendText(message);
            }
        }
    }

    public void sendAllMessage(String message) throws IOException {
        Collection<Session> values = sessionMap.values();
        for (Session value : values) {
            String s = JSONUtil.toJsonStr(message);
            value.getBasicRemote().sendText(s);
        }
    }

    public void sendVibrationMessage(String message) throws IOException {
        Collection<Session> values = sendVibrationTarget.values();
        for (Session value : values) {
            String s = JSONUtil.toJsonStr(message);
            value.getBasicRemote().sendText(s);
        }
    }

    /**
     * 发送消息
     *
     * @param session
     * @param message
     * @throws IOException
     */
    public void sendBytes(Session session, byte[] message) throws IOException {
        if (session != null) {
            synchronized (session) {
                session.getBasicRemote().sendBinary(ByteBuffer.wrap(message));
            }
        }
    }

    /**
     * 获取session
     *
     * @param token
     * @return
     */
    public Session getSession(String token) {
        return sessionMap.get(token);
    }

    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token) {
        this.session = session;
        sessionMap.put(token, session);
        log.info("{}:加入websocket", token);

    }

    @OnError
    public void OnError(@PathParam("token") String token, Throwable t) {
        log.info("“{}”直接退出", token);
    }

    @OnClose
    public void onClose(@PathParam("token") String token) throws IOException {
        sessionMap.remove(token);
        sendVibrationTarget.remove(token);
        if (sendVibrationTarget.size() == 0) {
            vibrationIsStart.set(false);
        }
        log.info("{}退出", token);
    }

    @OnMessage
    public void onMessage(byte[] data, Session session) {
        System.out.println("onMessage");
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        Timer timer = new Timer();
        try {
            if (ObjectUtil.isEmpty(message.trim())) {
                //心跳包
                session.getBasicRemote().sendText("");
                return;
            }
            //log.info("message______+"+message);
            final Message entity = JSONUtil.toBean(message, Message.class);
            if (entity == null) {
                log.info("解析失败");
                return;
            }
            //final String cmd = entity.getCmd();
//            LOG.info("message______from:{},to:{},cmd:{}",entity.getFrom(),entity.getTo(),entity.getCmd());
//            final Session destination = getSession(entity.getFrom());
            final Session destination = getSession(entity.getTo());

            //LOG.info("destination->{}",destination);

            switch (entity.getCmd()) {
                case "vibration_on_off":
                    if (destination == null) {
                        log.info("webSession失效");
                        sendMessage(session, "OFFLINE");
                        return;
                    }
                    synchronized (this) {
                        String jsonData = JSONUtil.toJsonStr(entity.getData());
                        AccelerationOnOrOff accelerationOnOrOff = JSONUtil.toBean(jsonData, AccelerationOnOrOff.class);
                        log.info("onOrOff->" + accelerationOnOrOff.getOnOrOff());
                        if ("on".equals(accelerationOnOrOff.getOnOrOff())) {
                            sendVibrationTarget.put(entity.getFrom(), getSession(entity.getFrom()));
                            log.info("vibrationIsStart.get()->" + vibrationIsStart.get());
                            if (vibrationIsStart.get()) {
                                return;
                            }
                            sendMessage(destination, JSONUtil.toJsonStr(entity));
                            vibrationIsStart.set(true);
                            log.info("发送振动启动消息成功");
                        } else {
                            sendVibrationTarget.remove(entity.getFrom());
                            log.info("sendVibrationTarget.size() ->" + sendVibrationTarget.size());
                            if (sendVibrationTarget != null && sendVibrationTarget.size() <= 0) {
                                sendMessage(destination, JSONUtil.toJsonStr(entity));
                                //模拟数据
                                /*V_Test.stop();*/
                                vibrationIsStart.set(false);
                                log.info("发送振动停止消息成功");
                            }

                        }
                    }
                    break;
                //pc => 网关
                case "START":
                case "STOP":
                case "SELF_INSPECTION":
                case "QUERY":
                case "GET":
                case "REGISTER":
                    //网关=>pc
                case "REPLY_QUERY_FILE":
                case "QUERY_FILE":
                case "GET_FILE":
                case "REPLY_REGISTER":
                case "REPLY":
                case "ECHO":
                case "REPLY_STOP":
                case "REPLY_SELF_INSPECTION":
                    if (destination == null) {
                        log.info("webSession失效");
                        sendMessage(session, "OFFLINE");
                        return;
                    }
                    ThreadUtil.execAsync(() -> {
                        try {
                            sendMessage(destination, JSONUtil.toJsonStr(entity));
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                        log.info("ws发送audioData消息成功");
                    });
                    break;
                //网关=>pc
                case "REPLY_GET_FILE":
                case "AUDIO":
                case "REPLY_START":
                    if (destination == null) {
                        log.info("webSession失效");
                        sendMessage(session, "OFFLINE");
                        return;
                    }
                    final String data = String.valueOf(entity.getData());
                    if (!data.contains("audio")) {
                        if (StrUtil.isNotEmpty(data)) {
                            byte[] ampData = Base64.decode(data);
                            ThreadUtil.execAsync(() -> {
                                try {
                                    sendBytes(destination, ampData);
                                    log.info("ws发送ampData消息成功");
                                } catch (IOException e) {
                                    throw new RuntimeException(e);
                                }
                            });
                        } else {
                            sendMessage(destination, "");
                        }
                        return;
                    }
                    JSONObject jsonObjectdata = JSONUtil.parseObj(entity.getData());
                    String channel = jsonObjectdata.getStr("channel");
                    String audio = jsonObjectdata.getStr("audio");
                    String type = jsonObjectdata.getStr("type");
                    //log.info("jsonObjectdata:{}",jsonObjectdata.toString());
                    byte[] bytes = Base64.decode(audio);
                    AudioFormat wavFormat = new AudioFormat(16000, 16, 1, true, false);
                    Librosa.WaveData<float[]> waveData = Librosa.loadBytes(bytes, wavFormat);
                    SpectrumEntity feature = null;
                    Map<String, Object> map = new HashMap<>();
                    if ("1".equals(type)) { //时域图
                        feature = Librosa.getFeature(waveData.data, 16000, 16, waveData.data.length / 2, waveData.data.length / 2, SpectrumEnums.SoundWav);
                    } else if ("2".equals(type)) {//时频图
                        feature = Librosa.getFeature(waveData.data, 16000, 16, waveData.data.length / 2, waveData.data.length / 2, SpectrumEnums.FFT);
                    } else if ("3".equals(type)) {//时频域图
                        feature = Librosa.getFeature(waveData.data, 16000, 16, waveData.data.length / 2, waveData.data.length / 2, SpectrumEnums.SpectrogramRT);

                        float[][] colorData = (float[][]) feature.getData();
                        float[] floats1 = colorData[0];
                        List<Float> colors = new ArrayList<>();
                        float min = 0;
                        double max;
                        max = Utils.max(colorData);
                        for (int i = 0; i < floats1.length; i++) {
                            for (float[] colorDatum : colorData) {
                                colors.add(colorDatum[i]);
                            }
                        }
                        List<Integer> colorDatas = new ArrayList<>();
                        double[][] colorMap = ColorMapUtil.colorMap;
                        for (Float color : colors) {
                            int a = Math.min(255, (int) Math.floor((((color - min) * 255.0) / (max - min))));
                            double[] doubles = colorMap[a];
                            int R = (int) (doubles[0] * 255);
                            int G = (int) (doubles[1] * 255);
                            int B = (int) (doubles[2] * 255);
                            int A = (int) (doubles[3] * 255);
                            colorDatas.add(R);
                            colorDatas.add(G);
                            colorDatas.add(B);
                            colorDatas.add(A);
                        }
                        map.put("originalData", colors);
                        map.put("colorData", colorDatas);
                    }
                    assert feature != null;
                    Object xAxis = feature.getXAxis();
                    Object yAxis = feature.getyAxis();
                    map.put("xAxis", xAxis);
                    map.put("yAxis", yAxis);
                    map.put("type", type);
                    map.put("channel", channel);
                    map.put("audio", bytes);
                    entity.setData(map);
                    ThreadUtil.execAsync(() -> {
                        try {
                            sendMessage(destination, JSONUtil.toJsonStr(entity));
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                        log.info("ws发送audioData消息成功");
                    });
                case "REPLY_TEMPERATURE_DATA":
                    JSONObject jsonObject = JSONUtil.parseObj(entity.getData());
                    BizTemperatureRecordParam temperature = new BizTemperatureRecordParam();
                    temperature = JSONUtil.toBean(jsonObject, BizTemperatureRecordParam.class);
//                    latestTemperature = temperature;
                    //后续开启线程调用
                    //  String newValue = TempConfigUtil.getValue(TempDictType.TEMP_RECORD_INTERVAL);
                    BizTemperatureConfig one = SpringUtil.getBean(BizTemperatureConfigService.class)
                            .lambdaQuery()
                            .eq(BizTemperatureConfig::getDictType, TempDictType.TEMP_RECORD_INTERVAL)
                            .one();
                    if (ObjectUtil.isNotNull(one) && ObjectUtil.isNotEmpty(one.getDictValue())) {
                        newValue = one.getDictValue();
                    }
                    if (TEMP_RECORD_INTERVAL_CACHE.get(temperature.getDeviceCode(), false) == null || "0".equals(0)) {
                        saveTemperatureData(temperature);
                        TEMP_RECORD_INTERVAL_CACHE.put(temperature.getDeviceCode(), "1", Long.valueOf(newValue) * 1000L);
                        // System.out.println(temperature.getDeviceCode() + "耗时ML：" + (System.currentTimeMillis() - start));
                        // start = System.currentTimeMillis();
                        log.warn("每次上报温度记录时间 >>> " + DateUtil.now());
                    }

                    //  System.out.println(temperature.getDeviceCode() + "耗时：" + (System.currentTimeMillis() - start));
                    break;
            }
        } catch (Exception e) {
            log.info(e.getMessage());
        }
    }


    //定时任务调用的方法
    public void saveTemperatureData(BizTemperatureRecordParam latestTemperature) {
//                public void saveTemperatureData() {
//        BizTemperatureRecordParam temperature = new BizTemperatureRecordParam();
//        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        temperature.setDeviceCode("1ae8ccef0d668192c296a039e3299110");
//        temperature.setNow(sdf.format(new Date()));
//        Map<String, Double> temperatureData = new HashMap<>();
//        Random random = new Random();
//        temperatureData.put("0", getRandomTemperature(random, 20, 30));
//        temperatureData.put("1", getRandomTemperature(random, 20, 30));
//        temperatureData.put("2", getRandomTemperature(random, 20, 30));
//        temperatureData.put("3", getRandomTemperature(random, 20, 30));
//        temperature.setTemperatureData(temperatureData);
//        latestTemperature = temperature;
        if (latestTemperature.getDeviceCode() != null) {
            //保存数据
            try {
                synchronized (this) {
                    // 同步的代码块
                    bizTemperatureRecordService.add(latestTemperature);
                    log.info("温度保存成功！");
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.info("温度保存失败！");
            }

            // log.info(latestTemperature.getDeviceCode());
        }
    }

    /**
     * 生成一个指定范围内的随机数，并保留两位小数。
     *
     * @param random   Random对象用于生成随机数
     * @param minValue 随机数的最小值（包含）
     * @param maxValue 随机数的最大值（不包含）
     * @return 带有两位小数的随机数
     */
    public static Double getRandomTemperature(Random random, double minValue, double maxValue) {
        double range = maxValue - minValue;
        double randomValue = minValue + (random.nextDouble() * range);
        long roundedValue = Math.round(randomValue * 100);
        return (double) roundedValue / 100;
    }

}
